#TODO
#rewrite "s" function to convert tuples
#
import Image, ImageFont, ImageDraw

from config import *

class Drawing:
	font = ImageFont.truetype("font.ttf", 15)
	text_color = 'red'
	colors = ['red', 'green', 'blue', 'yellow', 'brown', 'pink']
	counter = 0

	def __init__(self, cfg = Config()):
		self.cfg = cfg

	def draw_network(self, nl, pattern,  t):
		#head
		s = self.c
		cfg = self.cfg
		layers = nl.layers
		maximum_number_of_neurons = max(cfg.nna_npi, cfg.nna_npo, cfg.nna_nph)
		width = (cfg.nna_hlayers + 2) * 10
		height = maximum_number_of_neurons * 5
		terminal_weight_range = cfg.terminal_weight_max-cfg.terminal_weight_min
		im = Image.new('RGB', (s(width), s(height)), 'white')
		draw = ImageDraw.Draw(im)

		#calculating post spike
		post_spike_time = []
		for n in nl.output_layer:
			if n.spikes:
				post_spike_time.extend(n.spikes)
		if post_spike_time:
			post_spike_time = max(post_spike_time)
		else:
			post_spike_time = 'None'

		# drawing synapses
		for number_of_layer, layer in enumerate(layers):
			for number_of_neuron, neuron in enumerate(layer):
				xr = self.get_neuron_x(number_of_layer)
				yr = self.get_neuron_y(height, number_of_neuron, len(layer))
				if number_of_layer > 0:
					for synapse in neuron.ps:
						xe = self.get_neuron_x(synapse.emitter.number_of_layer)
						ye = self.get_neuron_y(height, synapse.emitter.number_of_neuron, len(layers[synapse.emitter.number_of_layer]))
						if synapse.terminals:
							color_picker = 255 - int(synapse.terminal_weights_avg() / terminal_weight_range * 255) % 256
						else:
							color_picker = 255
						scolor = (color_picker, color_picker, color_picker)
						draw.line((s(xr), s(yr), s(xe), s(ye)), scolor)
						dx = xr - xe
						dy = yr - ye
						# drawing terminals
						for terminal in synapse.terminals:
							r = 0.3 * terminal.weight
							xt = xe + dx * ((1 - terminal.delay) * 0.90 + 0.05)
							yt = ye + dy * ((1 - terminal.delay) * 0.90 + 0.05)
							draw.ellipse((s(xt - r), s(yt - r), s(xt + r), s(yt + r)), scolor, scolor)

		#drawing neurons
		for number_of_layer, layer in enumerate(layers):
			for number_of_neuron, neuron in enumerate(layer):
				xr = self.get_neuron_x(number_of_layer)
				yr = self.get_neuron_y(height, number_of_neuron, len(layer))
				r = 0.5
				neuron.spikes = neuron.spikes or []
				if t in neuron.spikes or neuron.x >= cfg.nu:
					color = 'red'
				else:
					color_picker = 255 - int(neuron.x / cfg.nu * 255) % 256
					color = (color_picker, 255, 255)
				draw.ellipse((s(xr - r), s(yr - r), s(xr + r), s(yr + r)), color, 'gray')
				draw.text((s(xr - r * 0.6), s(yr - r * 0.4)), '%1.3f' % neuron.x, font = self.font, fill = self.text_color)
		# drawing text
		draw.text((10, 10), 'time ' + str(post_spike_time), font = self.font, fill = self.text_color)
		del draw
		im.save(cfg.image_path + "m%03d.png" % (self.counter), cfg.image_extension)
		self.counter += 1

	def draw_data(self, data, real_labels, test_labels, alias):
		s = self.d
		colors = self.colors
		rheight = 110
		width = self.cfg.nna_npi * rheight - 9
		height = width

		im = Image.new('RGBA', (s(width), s(height)), 'white')
		draw = ImageDraw.Draw(im)
		#drawing grid
		for i in xrange(self.cfg.nna_npi):
			for j in xrange(self.cfg.nna_npi):
				draw.rectangle((s(i * rheight - 0.5), s(j * rheight - 0.5), s(i * rheight + 100.5), s(j * rheight + 100.5)), 'white', '#ccccee')
				draw.rectangle((s(i * rheight), s(j * rheight), s(i * rheight + 100), s(j * rheight + 100)), 'white', '#cccccc')
			draw.text((s(i * rheight + 50), s(i * rheight + 50)), str(i + 1), font = self.font, fill = self.text_color)

		for pattern_number, pattern in enumerate(data):
			for x_dimension_number, x_dimension in  enumerate(pattern):
				for y_dimension_number, y_dimension in enumerate(pattern):
					if x_dimension_number != y_dimension_number:
						x = x_dimension * 90 + 5 + x_dimension_number * rheight
						y = y_dimension * 90 + 5 + y_dimension_number * rheight
						r = 2
						try:
							color = colors[alias[real_labels[pattern_number]]]
						except:
							color = 'black'
						draw.ellipse((s(x-r), s(y-r), s(x + r), s(y + r)), color, color)
						r = 1
						try:
							color = colors[int(test_labels[pattern_number])]
						except:
							color = 'black'
						draw.ellipse((s(x-r), s(y-r), s(x + r), s(y + r)), color, color)
		del draw
		im.save(self.cfg.image_path + "correlation.png", self.cfg.image_extension)

	def draw_epsilons(self,output_layer):
		s = self.e
		cfg = self.cfg
		e = cfg.epsilon
		rheight = 110
		width = 500
		height = rheight * len(output_layer) + 10
		im = Image.new('RGBA', (s(width), s(height)), 'white')
		draw = ImageDraw.Draw(im)
		for neuron_number, neuron in enumerate(output_layer):
			for m in xrange(30):
				posx = float(m)/30
				draw.text((s(posx * (width - 20) + 10), s(rheight * (neuron_number +1))), str(posx * 2.0), font = self.font, fill = 'gray')
				draw.line((s(posx * (width - 20) + 10), s(rheight * (neuron_number +1) - 100), s(posx * (width - 20) + 10), s(rheight * (neuron_number +1)) + 4),  'gray')
			for n in xrange(5):
				ly = rheight * (neuron_number +1) - float(n+1)/5 *100
				draw.line((s(10), s(ly), s(width - 10), s(ly)), 'gray')
				draw.text((s(width - 10), s(ly)), str(float(n+1)/5), font = self.font, fill = 'gray')
			if neuron.pending_changes:
				for change_number, change in enumerate(neuron.pending_changes):
					ppx = 10
					ppy = rheight * (neuron_number +1)
					for x in xrange(width - 20):
						rx = float(x) / (width - 20)
						px = rx  * (width - 20) + 10
						d = rx * 2.0 - change
						py = (1 - e(d, neuron.pending_terminals[change_number].width, neuron.pending_terminals[change_number].weight)) *100 + rheight * neuron_number + 10
						scolor = int(neuron.pending_synapses[change_number][0])
						draw.line((s(ppx), s(ppy), s(px), s( py )), self.colors[scolor])
						ppx = px
						ppy = py
			draw.line((s(10), s(rheight * (neuron_number +1)), s(width - 10), s(rheight * (neuron_number +1))), 'black')
			#potential
			ppx = 10
			ppy = rheight * (neuron_number +1)
			for x in xrange(width - 20):
				rx = float(x) / (width - 20)
				px = rx  * (width - 20) + 10
				leverage = 1 - neuron.change_membrane_potential(rx * 2.0)
				if leverage <= 0:
					leverage = 0
					color = 'red'
				else:
					color = 'black'
				py = leverage*100 + rheight * neuron_number + 10
				draw.line((s(ppx), s(ppy), s(px), s( py )), color)
				ppx = px
				ppy = py




		del draw
		im.save(cfg.image_path + "e%03d.png" % (self.counter), cfg.image_extension)
		self.counter += 1

###############################################################################
# secondary functions
###############################################################################
	def get_neuron_x(self,number_of_layer):
		return (number_of_layer + 1) * 10-4.5

	def get_neuron_y(self, height, number_of_neuron, len_layer):
		return height / 2 + (number_of_neuron + 1 - len_layer / 2.0) * 5.0 - 2.0

	def c(self,value):
		return int(value * self.cfg.image_network_scale)

	def d(self,value):
		return int(value * self.cfg.image_data_scale)

	def e(self,value):
		return int(value * self.cfg.image_epsilon_scale)

	def clean_folder(self):
		import os
		folder = self.cfg.image_path
		for the_file in os.listdir(folder):
			file_path = os.path.join(folder, the_file)
			try:
				if os.path.isfile(file_path):
					os.unlink(file_path)
			except Exception, e:
				print e